/* GameBoard.java */
/* Test commit */
package player;

/** GameBoard is a class that represents the game board of Network.  A GameBoard is a 2d array of GameBoardCells (8 x 8 2d array).
 */

public class GameBoard {

	protected GameBoardCell[][] board;
	private GameBoardCell[][] allChips = new GameBoardCell[2][10];

	/** CONSTANTS listed below here	 **/
	protected int COMPUTER;
	protected int HUMAN;

	protected final int WIDTH = 8;
	protected final int HEIGHT = 8;

	public static final int CORNER = -2;
	public static final int EMPTY = -1;
	public static final int BLACK = 0;
	public static final int WHITE = 1;

	private static final int N = 0;
	private static final int NE = 1;
	private static final int E = 2;
	private static final int SE = 3;
	private static final int S = 4;
	private static final int SW = 5;
	private static final int W = 6;
	private static final int NW = 7;

	private int[] MAX_SCORE = new int[2];

	/**GameBoard() is the constructor that returns a new blank GameBoard.
	 * The 4 corner GameBoardCell objects are marked with color = CORNER (and thus rendering them invalid).
	 * all the other cells are market valid with color = EMPTY;
	 * @param player is the color of the MachinePlayer (either BLACK or WHITE).
	 */
	public GameBoard(int player) {   
		this.board = new GameBoardCell[WIDTH][HEIGHT];
		for (int y=0; y < HEIGHT; y++) {
			for (int x=0; x < WIDTH; x++) {
				GameBoardCell cell = new GameBoardCell(x,y);
				board[x][y] = cell;
				cell.x = x;
				cell.y = y;
			}
		}
		assert player == WHITE || player == BLACK: "player is not WHITE nor BLACK";
		COMPUTER = player;
		HUMAN = otherPlayer(player);
		MAX_SCORE[COMPUTER] = 100;
		MAX_SCORE[HUMAN] = -100;
	}

	/**INTERFACE METHOD. isValidMove returns a boolean of whether move m is valid on this current GameBoard.
	 * 1) Checks for CORNER cells. No moves can ever involve corner cells;
	 * 2) Checks to see that no chip may be placed in the goal cells of the opposite color.
	 * 3) Checks that no chip may be placed in a square that is already occupied. 
	 * @param m is a Move object that we are validating on our current GameBoard configuration.
	 * @param player tells us whose turn it is, GameBoard.BLACK or GameBoard.WHITE. Therefore we can determine whether this move can add cells to certain areas and disqualify it if it
	 * is trying to modify a cell with the opposite color.
	 */

	public boolean isValidMove (Move m, int player) {
		GameBoardCell cell = board[m.x1][m.y1];
		int chipsOnBoard = objectsInArray(allChips[player]);
		if (chipsOnBoard < 10 && m.moveKind == Move.STEP) {
			return false;
		}
		if (chipsOnBoard >= 10 && m.moveKind == Move.ADD) {
			return false;  
		}
		if (cell.color == CORNER || (m.moveKind == Move.STEP && board[m.x2][m.y2].color == CORNER)) {
			return false;
		}
		if ((player == BLACK && cell.whiteGoal == true) || (player==WHITE && cell.blackGoal == true)) {
			return false;
		}
		if ((m.moveKind == Move.ADD && cell.color != EMPTY) || (m.moveKind==Move.STEP && cell.color != EMPTY)) {
			return false;
		}
		if ((m.moveKind == Move.ADD && willBeCluster(m.x1,m.y1,player))) {
			return false;
		}               
		if (m.moveKind == Move.STEP) {
			if (board[m.x2][m.y2].color != player) {
				return false;
			}
			removeChip(m.x2, m.y2);
			boolean valid = !willBeCluster(m.x1, m.y1, player);
			addChip(m.x2, m.y2, player);
			if (!valid) {   // Only return false here.  Make sure every case is checked.
				return false;
			}
		}

		return true;

	}

	/**INTERFACE METHOD. doMove takes a move and mutates this GameBoard object to do this move (ex: move chips, remove chips, add chips)
	 * First checks if the move m is valid. If not, it doesn't do anything. If it is valid it does the move on the current GameBoard.
	 * Note that applications should update the game board (ADD or STEP chips) only by calling doMove.
	 * @param m is the Move object we will be doing on our GameBoard.
	 * @param player tells us whose turn it is. GameBoard.BLACK or GameBoard.WHITE.         
	 */
	public void doMove(Move m, int player) {
		if (!isValidMove(m,player)) {
			return;
		}
		if (m.moveKind==Move.ADD) {
			addChip(m.x1,m.y1,player);
		} else if (m.moveKind == Move.STEP) {
			stepChip(m.x1,m.y1,m.x2,m.y2);
		}
		updateChipConnections();
	}       

	/**INTERFACE METHOD.  undoMove undos move m.  If m was a STEP move, undoMove steps the chip back to its original location.  If it was
	 * an ADD move, the chip is removed.
	 * WARNING: It is NOT the move to do. ex: m would be the previously done move so we have to 
	 * do steps in "reverse" because undoing is the reverse of doing that move.
	 * @param Move m is the move to be undone. 
	 */
	protected void undoMove(Move m) {
		if (m.moveKind == Move.ADD) {
			removeChip(m.x1,m.y1);
		} else if (m.moveKind == Move.STEP) {
			stepChip(m.x2,m.y2,m.x1,m.y1);
		}
		updateChipConnections(); 
	}

	/**INTERFACE METHOD: getChipColor gets the color of the chip at x and y
	 * Returns the color of the chip at position (x,y).
	 */
	public int getChipColor(int x,int y) {
		if (x < 0 || x >= 8 || y < 0 || y >= 8) {
			return Integer.MIN_VALUE;
		}
		return board[x][y].color;
	}

	/** addChip changes a GameBoardCell at coordinate (x,y) to @param color (an integer). Move was already checked by doMove (through a call to isValidMove) and is assumed
	 * to be valid.
	 * @param x is the x coordinate of the GameBoardCell we are modifying
	 * @param y is the y coordinate of the GameBoardCell we are modifying
	 * @param color is the integer that represents the color of the chip.
	 */
	private void addChip(int x, int y, int color) {
		board[x][y].color = color;  
		insertObjectIntoArray(board[x][y],allChips[color]);
	}

	/** removeChip removes a chip from the board if the chip is not EMPTY or not CORNER.  Move was already checked by doMove (through a call to isValidMove) and is assumed
	 * to be valid.
	 * @param x is the x coordinate of the GameBoardCell we are modifying
	 * @param y is the y coordinate of the GameBoardCell we are modifying
	 */
	private void removeChip(int x, int y) {
		int currColor = board[x][y].color; 
		board[x][y].color = EMPTY;
		removeObjectFromArray(board[x][y],allChips[currColor]);
	}

	/** stepChip moves a chip from (x2, y2) to (x1, y1).  Move was already checked by doMove (through a call to isValidMove) and is assumed
	 * to be valid.
	 * @param x1, y1 are the (new) coordinates which we are moving the chip to.
	 * @param x2, y2 are the (old) coordinates of chip to be moved.
	 */
	private void stepChip(int x1, int y1, int x2, int y2) {
		int oldChipcolor = getChipColor(x2,y2);
		this.removeChip(x2,y2);
		this.addChip(x1,y1,oldChipcolor);
	}

	/**INTERFACE METHOD: allValidMoves returns an array of all possible moves depending on which player's turn it is.
	 * @param board is the GameBoard
	 * @param player is which player's turn it is. (Either BLACK or WHITE [0 or 1])
	 * @return nicevalidMoves[] an array with all possible moves. If number of chips on the board is less than 10, then isValidMove is called on the move.
	 * If the number of chips is greater then 10, then we are creating a step move and isValidMove is called on every chip existing on the board to create an array of valid moves.
	 */
	public Move [] allValidMoves(int player) {
		int z = 0;
		Move validMoves[] = new Move[1000]; 
		Move niceValidMoves[];
		Move myMove = new Move(0,0); //initialize it to some dummy value that won't be used.

		for (int y=0; y< HEIGHT; y++) {
			for (int x=0; x< WIDTH; x++) {
				if (objectsInArray(this.allChips[player]) < 10){ //still in the ADD phase of the game (less than 10 chips on the board)
					myMove = new Move(x,y);
					if (isValidMove(myMove, player)) {
						validMoves[z] = myMove;
						z++;
					}
				} 
				else if (objectsInArray(this.allChips[player]) == 10) { // in the STEP phase of the game
					for (int a = 0; a<10; a++) {
						myMove = new Move (x, y, this.allChips[player][a].x,this.allChips[player][a].y);
						if (isValidMove(myMove, player)) {
							validMoves[z] = myMove;
							z++;
						}
					}
				}
			}
		}
		int c = this.objectsInArray(validMoves);
		niceValidMoves = new Move [c];
		for (int d=0; d<c; d++) {
			niceValidMoves[d]=validMoves[d];
		}
		return niceValidMoves;
	}

	/**
	 * INTERFACE METHOD. hasNetwork returns whether or not a player has a network.  A network must:
	 * (1) Be composed of at least 6 chips.
	 * (2) Begin and end in the color's goal area, with no other chips in same goal.
	 * (3) Must change direction at each chip.
	 * @param player is the color of the player, denoted with constants BLACK or WHITE.
	 * @return true if the player has a network; false if the player does not.
	 */

	protected boolean hasNetwork(int player) {
		GameBoardCell[] goalChips = new GameBoardCell[10]; //all the goal chips are visited chips.
		boolean success = false;
		int[] allValidDirections = newDirectionArray();

		for (GameBoardCell chip : allChips[player]) {
			if (chip != null && (chip.blackGoal || chip.whiteGoal) && (chip.x == 0 || chip.y == 0)) { //Only adds the top row for BLACK and LEFT column for WHITE (depending on player).
				insertObjectIntoArray(chip,goalChips);
			}
		}
		int direction = 0;
		GameBoardCell nextChip;
		int[] newValidDirections;

		for (GameBoardCell chipInGoal : goalChips) {
			direction = 0;
			if (chipInGoal != null) {
				while (direction < 8 ) {
					nextChip = chipInGoal.connections[direction];
					if (nextChip != null && allValidDirections[direction] == 1) { //ALSO checks if the new direction is valid.
						newValidDirections = validDirection(direction);
						success = checkNetwork(copyArray(goalChips),1,newValidDirections,nextChip); //newVisitedChips is updated already by now with the current chip inside the array.
						if (success == true) {
							return true;
						}
					}
					direction++;
				}
			}
		}
		return false;
	}

	/**
	 * checkNetwork is the helper for hasNetwork, which recursively follows connections to attempt to build a network.
	 * @param visitedChips GameBoardCell[] of chips that we have already visited.
	 * @return true if the current chain of connections represents a network, false otherwise.
	 */

	private boolean checkNetwork(GameBoardCell[] visitedChips, int currCount,int[] validDirections, GameBoardCell chip) {
		boolean hasNetwork;
		currCount = currCount+1;

		for (GameBoardCell cell : visitedChips) { //checks to see if we have visited a chip we already visited.
			if (cell == chip) {
				return false;
			}
		}

		if ((chip.whiteGoal == true || chip.blackGoal == true) && currCount >= 6) { //because chips will only be connected to chips of the same color, checking both goals is okay
			return true;
		} else if ((chip.whiteGoal == true || chip.blackGoal == true) && currCount < 6) {
			return false;
		}

		GameBoardCell[] newVisitedChips = copyArray(visitedChips);
		boolean inserted = insertObjectIntoArray(chip,newVisitedChips);
		if (inserted == false) { //array is full and we haven't won so obviously we don't have a network.
			return false;
		}

		int[] newValidDirections = new int[8]; 
		int direction = 0;
		GameBoardCell nextChip;

		while (direction < 8 ) {
			nextChip = chip.connections[direction];
			if (nextChip != null && validDirections[direction] == 1) { //ALSO checks if the new direction is valid.
				newValidDirections = validDirection(direction);
				hasNetwork = checkNetwork(newVisitedChips,currCount,newValidDirections,nextChip); //newVisitedChips is updated already by now with the current chip inside the array.
				if (hasNetwork == true) {
					return true;
				}
			}
			direction++;
		}


		return false;
	}

	/**
	 * numberOfConnections returns the number of PAIRED connections between chips. Two chips that are connected form one paired connection. This function double counts each pair but that's okay.
	 * @param player is the int of the player (WHITE OR BLACK) we are counting connections for. 
	 * @return the number of paired connections
	 */
	private int numberOfConnections(int player) {
		int connections = 0;

		for (GameBoardCell chip : allChips[player]) {
			if (chip != null) {
				for (GameBoardCell connectedChip : chip.connections) {
					if (connectedChip != null) {
						connections++;
					}
				}
			}
		}
		return connections;
	}

	/** chipsInFarGoal returns the number of chips in the far goal, which is defined to be RIGHT side for WHITE and bottom side for BLACK. The other goal strip for each player is the INITIAL goal side.
	 * In essence we are treating the two sides of the goal's for each player differently, like a start and end network path where start and ending sides are distinct.
	 * @param player is the color of the player we are counting chips for
	 * @return the number chips in the far goal as defined in this description.
	 */
	private int chipsInFarGoal(int player) {
		int chips = 0;
		if (player == BLACK) {
			for (int x = 1; x < 8; x++) {
				if (getChipColor(x,HEIGHT-1) == player) {
					chips++;
				}
			}
		} else if (player == WHITE) {
			for (int y = 1; y < 8; y++) {
				if (getChipColor(WIDTH -1,y) == player) {
					chips++;
				}
			}
		}
		return chips;
	}

	/**
	 * validDirection returns an array of integers corresponding to the valid directions 
	 * for a following chip in a sequence of chips (to find a Network).  Invalid directions are denoted with a 0, valid directions are denoted 
	 * with a 1. A direction corresponds to the INDEX of the array directionArr (index 0 = NORTH. NORTH can be valid if directionArr[0]=1 or false if directionArr[0] = 0)
	 * 
	 * ex: Two chips that were connected (5,0) (5,2) cannot have a third cell (5,4) connected in any valid network. Thus we mark the SAME direction (SOUTH in our case) AND
	 * the opposite direction (NORTH, denoted by direction + 4 mod 8) invalid (with 0)
	 * WARNING: The returned array is ORDERED, and each cell corresponds with a specific 
	 * direction!
	 * @param direction is the direction in which we just traveled and thus is marked invalid.
	 * @return an array of integers (0 or 1) which determine whether a direction is valid.
	 * Use the constants in GameBoard.java for directions.
	 */
	private int[] validDirection(int direction) {
		assert (direction >= 0 && direction < 8) : "Invalid direction provided.  Direction provided was " + direction;
		int[] directionArr = new int[8];
		for (int i = 0; i < directionArr.length; i++) {
			directionArr[i] = 1;
		}

		directionArr[wrapDirection(direction)] = 0;
		directionArr[wrapDirection(direction + 4)] = 0;	// Invalidate opposite direction as well.
		return directionArr;
	}

	private int[] newDirectionArray() {
		int[] newArray = new int[8];
		for (int i =0; i < newArray.length; i++) { 
			newArray[i] = 1;
		}
		return newArray;
	}
	/**
	 * wrapDirection takes a direction (which may be invalid) and converts it into a valid 
	 * direction, i.e. one that falls in the appropriate range of 0-7.
	 * @param direction is the direction that needs to be wrapped into the correct range.
	 * @return a direction that falls within the correct range, i.e. 0-7.
	 */
	private int wrapDirection(int direction) {
		assert (direction >= 0) : "Direction is invalid.  Direction provided is : " + direction;
		return direction % 8;
	}

	/**
	 * INTERFACE METHOD.  objectsInArray takes a single list of objects (i.e. EITHER black's or white's list of chips) and 
	 * returns the number of non-null objects inside.
	 * WARNING: array canNOT be a 2-D array!  It MUST be a 1-D array of objects.
	 * @param array is a single list of objects
	 * @return Returns the length of non-empty elements of array.
	 */
	protected int objectsInArray(Object[] array) {
		int chipCount = 0;
		for (Object cell : array) {
			if (cell != null) {
				chipCount++;
			}
		}
		return chipCount;
	}

	/**
	 * INTERFACE METHOD.  updateChipConnections walks through and updates each player's chip's connections with all other 
	 * chips on the board.  A connection must be in one of the 8 cardinal directions (except in the goal 
	 * areas, where it cannot be connected to any other goal chip), and must not have any other chips in
	 * the way.  The chip's connections list is updated at end of execution.
	 */
	protected void updateChipConnections() {
		for (int i = 0; i < allChips[BLACK].length; i++) {
			if (allChips[BLACK][i] != null) {
				GameBoardCell curr = allChips[BLACK][i];
				curr.connections = connectChip(BLACK, curr);
				board[curr.x][curr.y].connections = curr.connections;
			}
		}
		for (int i = 0; i < allChips[WHITE].length; i++) {
			if (allChips[WHITE][i] != null) {
				GameBoardCell curr = allChips[WHITE][i];
				curr.connections = connectChip(WHITE, curr);
				board[curr.x][curr.y].connections = curr.connections;
			}
		}
	}

	/**
	 * connectChip is a helper function which loops through the board for each chip to find the connections 
	 * for that particular chip and returns an array of ORDERED connections.
	 * 
	 * WARNINGS: 
	 * (i)  North and west end goal chips are connected only to chips not in the same goal area.
	 * (ii) South and east end goal chips are not connected to other chips.  This is because valid networks 
	 * cannot contain other goal chips.
	 * (iii)No chips are connected to north and west end goal chips.
	 * (iv) The returned array of connections is ORDERED based on the direction: 0 for N, 1 for NE, 2 for E, etc.
	 * @param player is the color of the player whose chips we are inspecting.
	 * @param currChip is the current chip being connected.
	 * @return An ORDERED array of connections for the chip.
	 */
	private GameBoardCell[] connectChip(int player, GameBoardCell currChip) {
		assert (player == BLACK || player == WHITE) : "Invalid player given, player given was " + player;
		GameBoardCell[] connections = new GameBoardCell[8];

		connections[N] = checkNorth(player, currChip);
		connections[NE] = checkNortheast(player, currChip);
		connections[E] = checkEast(player, currChip);
		connections[SE] = checkSoutheast(player, currChip);
		connections[S] = checkSouth(player, currChip);
		connections[SW] = checkSouthwest(player, currChip);
		connections[W] = checkWest(player, currChip);
		connections[NW] = checkNorthwest(player, currChip);

		return connections;
	}

	/**
	 * checkNorth is a helper function which checks for chips to the north, in accordance to the rules 
	 * specified in connectChip.
	 * @param player is the color of the chips we are inspecting.
	 * @param currChip is the current chip we are trying to connect.
	 * @return a GameBoardCell which lies to the north of currChip if it exists, or null if an opponent's 
	 * chip is encountered or the 'checkable' area has been searched.
	 */
	private GameBoardCell checkNorth(int player, GameBoardCell currChip) {
		assert (player == BLACK || player == WHITE) : "Invalid player given, player given was " + player;
		int xcoord = currChip.x;
		int ycoord = currChip.y;

		for (int y = ycoord-1; y >= 0; y--) {   
			if (board[xcoord][y].color == player) { // If player's chip is reached, return it.
				return board[xcoord][y];
			}
			else if (board[xcoord][y].color == otherPlayer(player)) {       // If opponent's chip is reached, stop checking and null.
				return null;
			}
		}
		return null;    // Finished checking without encountering other chips that can be connected, null.
	}

	/**
	 * checkNortheast is a helper function which checks for chips to the northeast, in accordance to the rules 
	 * specified in connectChip.
	 * @param player is the color of the chips we are inspecting.
	 * @param currChip is the current chip we are trying to connect.
	 * @return a GameBoardCell which lies to the northeast of currChip if it exists, or null if an opponent's 
	 * chip is encountered or the 'checkable' area has been searched.
	 */
	private GameBoardCell checkNortheast(int player, GameBoardCell currChip) { 
		assert (player == BLACK || player == WHITE) : "Invalid player given, player given was " + player;
		int xcoord = currChip.x;
		int ycoord = currChip.y;

		int x = xcoord+1, y = ycoord-1; // Initialize to next spot to check.
		while (x < WIDTH && y >= 0) {    
			if (board[x][y].color == player) {      // If color matches, return the chip.
				return board[x][y];
			}
			else if (board[x][y].color == otherPlayer(player)) {    // If color is opponent's chip, stop checking, null.
				return null;
			}
			x++;
			y--;
		}
		return null;    // Finished checking without encountering other chips that can be connected, null.  Corners come here.
	}

	/**
	 * checkEast is a helper function which checks for chips to the east, in accordance to the rules 
	 * specified in connectChip.
	 * @param player is the color of the chips we are inspecting.
	 * @param currChip is the current chip we are trying to connect.
	 * @return a GameBoardCell which lies to the east of currChip if it exists, or null if an opponent's 
	 * chip is encountered or the 'checkable' area has been searched.
	 */
	private GameBoardCell checkEast(int player, GameBoardCell currChip) { 
		assert (player == BLACK || player == WHITE) : "Invalid player given, player given was " + player;
		int xcoord = currChip.x;
		int ycoord = currChip.y;

		for (int x = xcoord+1; x < WIDTH; x++) { 
			if (board[x][ycoord].color == player) { // If player's chip is reached, return it.
				return board[x][ycoord];
			}
			else if (board[x][ycoord].color == otherPlayer(player)) {       // If opponent's chip is reached, stop checking and null.
				return null;
			}
		}
		return null;    // Finished checking without encountering other chips that can be connected, null.
	}

	/**
	 * checkSoutheast is a helper function which checks for chips to the southeast, in accordance to the rules 
	 * specified in connectChip.
	 * @param player is the color of the chips we are inspecting.
	 * @param currChip is the current chip we are trying to connect.
	 * @return a GameBoardCell which lies to the southeast of currChip if it exists, or null if an opponent's 
	 * chip is encountered or the 'checkable' area has been searched.
	 */
	private GameBoardCell checkSoutheast(int player, GameBoardCell currChip) { 
		assert (player == BLACK || player == WHITE) : "Invalid player given, player given was " + player;
		int xcoord = currChip.x;
		int ycoord = currChip.y;

		int x = xcoord+1, y = ycoord+1; // Initialize to next spot to check.
		while (x < WIDTH && y < HEIGHT) {
			if (board[x][y].color == player) {      // If color matches, return the chip.
				return board[x][y];
			}
			else if (board[x][y].color == otherPlayer(player)) {    // If color is opponent's chip, stop checking, null.
				return null;
			}
			x++;
			y++;
		}
		return null;    // Finished checking without encountering other chips that can be connected, null.
	}

	/**
	 * checkSouth is a helper function which checks for chips to the south, in accordance to the rules 
	 * specified in connectChip.
	 * @param player is the color of the chips we are inspecting.
	 * @param currChip is the current chip we are trying to connect.
	 * @return a GameBoardCell which lies to the south of currChip if it exists, or null if an opponent's 
	 * chip is encountered or the 'checkable' area has been searched.
	 */
	private GameBoardCell checkSouth(int player, GameBoardCell currChip) { 
		assert (player == BLACK || player == WHITE) : "Invalid player given, player given was " + player;
		int xcoord = currChip.x;
		int ycoord = currChip.y;

		for (int y = ycoord+1; y < HEIGHT; y++) { // Check black's south goal area.
			if (board[xcoord][y].color == player) { // If player's chip is reached, return it.
				return board[xcoord][y];
			}
			else if (board[xcoord][y].color == otherPlayer(player)) {       // If opponent's chip is reached, stop checking and null.
				return null;
			}
		}
		return null;    // Finished checking without encountering other chips that can be connected, null.
	}

	/**
	 * checkSouthwest is a helper function which checks for chips to the southwest, in accordance to the rules 
	 * specified in connectChip.
	 * @param player is the color of the chips we are inspecting.
	 * @param currChip is the current chip we are trying to connect.
	 * @return a GameBoardCell which lies to the southwest of currChip if it exists, or null if an opponent's 
	 * chip is encountered or the 'checkable' area has been searched.
	 */
	private GameBoardCell checkSouthwest(int player, GameBoardCell currChip) { 
		assert (player == BLACK || player == WHITE) : "Invalid player given, player given was " + player;
		int xcoord = currChip.x;
		int ycoord = currChip.y;

		int x = xcoord-1, y = ycoord+1; // Initialize to next spot to check.
		while (x >= 0 && y < HEIGHT) {
			if (board[x][y].color == player) {      // If color matches, return the chip.
				return board[x][y];
			}
			else if (board[x][y].color == otherPlayer(player)) {    // If color is opponent's chip, stop checking, null.
				return null;
			}
			x--;
			y++;
		}
		return null;    // Finished checking without encountering other chips that can be connected, null.
	}

	/**
	 * checkWest is a helper function which checks for chips to the west, in accordance to the rules 
	 * specified in connectChip.
	 * @param player is the color of the chips we are inspecting.
	 * @param currChip is the current chip we are trying to connect.
	 * @return a GameBoardCell which lies to the west of currChip if it exists, or null if an opponent's 
	 * chip is encountered or the 'checkable' area has been searched.
	 */
	private GameBoardCell checkWest(int player, GameBoardCell currChip) {
		assert (player == BLACK || player == WHITE) : "Invalid player given, player given was " + player;
		int xcoord = currChip.x;
		int ycoord = currChip.y;

		for (int x = xcoord-1; x >= 0; x--) {   // Do NOT check white's west goal area!
			if (board[x][ycoord].color == player) { // If player's chip is reached, return it.
				return board[x][ycoord];
			}
			else if (board[x][ycoord].color == otherPlayer(player)) {       // If opponent's chip is reached, stop checking and null.
				return null;
			}
		}
		return null;    // Finished checking without encountering other chips that can be connected, null.
	}

	/**
	 * checkNorthwest is a helper function which checks for chips to the northwest, in accordance to the rules 
	 * specified in connectChip.
	 * @param player is the color of the chips we are inspecting.
	 * @param currChip is the current chip we are trying to connect.
	 * @return a GameBoardCell which lies to the northwest of currChip if it exists, or null if an opponent's 
	 * chip is encountered or the 'checkable' area has been searched.
	 */
	private GameBoardCell checkNorthwest(int player, GameBoardCell currChip) {
		assert (player == BLACK || player == WHITE) : "Invalid player given, player given was " + player;
		int xcoord = currChip.x;
		int ycoord = currChip.y;

		int x = xcoord-1, y = ycoord-1; // Initialize to next spot to check.
		while (x >= 0 && y >= 0) {      // Do NOT check white's west goal area OR black's north goal area.
			if (board[x][y].color == player) {      // If color matches, return the chip.
				return board[x][y];
			}
			else if (board[x][y].color == otherPlayer(player)) {    // If color is opponent's chip, stop checking, null.
				return null;
			}
			x--;
			y--;
		}
		return null;    // Finished checking without encountering other chips that can be connected, null.
	}

	/** INTERFACE METHOD.  evaluate evaluates a board at any configuration and returns a double between [-100.0,100.0] (inclusive on both ends)
	 * that signifies the score of the board where 100.0 is a guaranteed win in one turn for the player that owns this GameBoard object. -100.0 is a one-turn
	 * win for the opposite player
	 * @param palyer is the COLOR of the MachinePlayer (who is the only one supposedly using this GameBoard).
	 * @return the evaluated score between [-100.0,100.0]  
	 */
	public double evaluate(int player) {

		int myChips = objectsInArray(allChips[player]);
		int myConnections = numberOfConnections(player);
		int opponentConnections = numberOfConnections(otherPlayer(player));
		double score = 0;

		/** First checks for winning networks */
		if (hasNetwork(player)) {
			return MAX_SCORE[player];
		} else if (hasNetwork(otherPlayer(player))) {
			return MAX_SCORE[otherPlayer(player)];
		}

		/** updates the goalChips **/
		GameBoardCell[] goalChips = new GameBoardCell[10]; //all the goal chips are visited chips.
		for (GameBoardCell chip : allChips[player]) {
			if (chip != null && (chip.blackGoal || chip.whiteGoal) && (chip.x == 0 || chip.y == 0)) { //Only adds the top row for BLACK and LEFT column for WHITE (depending on player).
				insertObjectIntoArray(chip,goalChips);
			}
		}
		/** Early game moves: myChips <= 3 **/
		int farGoalChips = chipsInFarGoal(player); //goalChips are only the INITIAL side (top for BLACK, and left side for WHITE). this is for the OTHER side.
		double chipsInMyGoal = objectsInArray(goalChips);
		if (myChips <= 3) {
			if (chipsInMyGoal <= 1) {
				score = 0;
			} 
			if (farGoalChips < 1) {
				score = 0;
			}
		}  
		/** Mid-game moves: chips 4 and onward **/
		if (myChips > 3) {
			if (chipsInMyGoal <= 1) {
				score = score - 0.3 *score;
			} 
			if (farGoalChips < 2) {                         
				score = score - 0.3 * score;
			} 
		}
		/** Connection Optimization **/
		score = score + 3 * myConnections - 2.5 * (opponentConnections);
		return scoreProcessor(score,player);
	}

	/**
	 * scoreProcessor takes a score calculated by evaluate and modifies it based on who the player is.
	 * If the player is the COMPUTER, scoreProcessor simply returns the score, since better moves have 
	 * higher (more positive) scores.  Otherwise, scoreProcessor returns the negative score, since better
	 * moves for the computer (worse moves for the opponent) have lower (more negative) scores.
	 * @param score is the raw score calculated by evaluate.
	 * @param player is the player whose currently evaluating moves.
	 * @return a processed score which works with minimax
	 */
	private double scoreProcessor(double score, int player) {
		assert player == BLACK || player == WHITE: "score processing an invalid player: " + player;
		if (player == COMPUTER) {
			return score;
		} else {
			return -1 * score;
		}
	}

	/** INTERFACE METHOD.  minimax is a function that performs and alpha-beta pruning of possible gameboards and returns a MoveAndScore object with the optimal move and associated score.
	 * 
	 * @param player is the player we are finding a best move for 
	 * @param alpha is the upper bound of best score. Updated at max levels (Machine turn)
	 * @param beta is the lower bound of the worst score. Updated at min levels
	 * @return MoveAndScore object which has a .move attribute with the "best" move and .score attribute with the "best" score.
	 */
	public MoveAndScore minimax (int player, double alpha, double beta, int depth,int maxSearchDepth,MoveAndScore passedBest) {
		assert maxSearchDepth > 0 && depth < maxSearchDepth && depth >= 0: "depth parameters are invalid (negative or 0)";
		MoveAndScore myBest = new MoveAndScore();
		MoveAndScore reply;
		double score ;

		if (depth > maxSearchDepth) {
			passedBest.score = evaluate(player);
			return passedBest;
		}
		Move[] validMoves = allValidMoves(player);

		if (hasNetwork(player) || hasNetwork(otherPlayer(player)) || validMoves.length == 0) { 
			score =  evaluate(player);
			return new MoveAndScore(score,new Move());
		}

		if (player == COMPUTER) {
			myBest.score = alpha;
		} else {
			myBest.score = beta;
		}

		for (Move m : validMoves) {
			doMove(m,player);
			reply = minimax(otherPlayer(player), alpha, beta, depth + 1, maxSearchDepth,myBest);
			undoMove(m);
			if ( (player == COMPUTER) && (reply.score > myBest.score)) {
				myBest.move = m;
				myBest.score = reply.score;
				alpha = reply.score;
			} else if ((player == HUMAN) && (reply.score < myBest.score)) {
				myBest.move = m;
				myBest.score = reply.score;
				beta = reply.score;

			}
			if (alpha >= beta) {
				return myBest;
			}
		}           
		myBest.score = myBest.score * 0.9; //to account for lower turns.
		return myBest;
	}

	/**
	 * willBeCluster calls countClusters to inspect whether or not a cluster of 3 or more chips of the same color will be formed AFTER
	 * a chip is inserted at position (x, y).  countClusters() is a recursive helper function.
	 * @param x is the x-coordinate of the position we wish to add the chip to (but have not yet added).
	 * @param y is the y-coordinate of the position we wish to add the chip to (but have not yet added).
	 * @param color is the color of the chip we are adding and are interested if it will form a cluster with cells of the same color.
	 * @return true if a cluster will be formed, false otherwise.
	 */
	private boolean willBeCluster(int x, int y, int color) {
		return countClusters(x, y, x, y, color, 0);
	}
	/**
	 * countClusters is a recursive helper function which determines whether or not a cluster will be formed.
	 * @param prevX is the x-coordinate from the previous call to countClusters.  This is necessary to prevent the program from 
	 * continuously alternating between a pair of chips, since a chip at (prevX, prevY) will not be inspected again.
	 * @param prevY is the y-coordinate from the previous call to countClusters.  This is necessary to prevent the program from
	 * continuously alternating between a pair of chips, since a chip at (prevX, prevY) will not be inspected again.
	 * @param x is the x-coordinate of the position we are interested in inspecting.
	 * @param y is the y-coordinate of the position we are interested in inspecting.
	 * @param color is the color of the chip we are adding and are interested if it will form a cluster with cells of the same color.
	 * @param clusterCount is the number of neighbors that have been counted.  When initially called by willBeCluster, clusterCount is
	 * set to 0.  When countClusters is called recursively, clusterCount is 1 to compensate for the fact that we have not actually 
	 * added the chip we were originally interested in testing.
	 * @return true if a cluster will be formed, false otherwise.
	 */
	private boolean countClusters(int prevX, int prevY, int x, int y, int color, int clusterCount) {
		int xToCheck = prevX, yToCheck = prevY;

		for (int i = x-1; i <= x+1; i++) {
			for (int j = y-1; j <= y+1; j++) {
				if (i == x && j == y) {
					continue;
				}     // Don't check own position.  Important for recursive call of countClusters().
				if (getChipColor(i, j) == color) {      // If color matches, increment clusterCount and save coordinates to check recursively.
					clusterCount++;
					xToCheck = i;
					yToCheck = j;
				}
				if (clusterCount >= 2) {        // If at any point we hit 2 neighbors, MUST be cluster.  Don't check any further.
					return true;
				}
			}
		}
		if (clusterCount == 1 && (xToCheck != prevX || yToCheck != prevY)) {    // Only reason to check recursively is if 1 neighbor.
			return countClusters(x, y, xToCheck, yToCheck, color, 1);
		}
		return false;   // clusterCount == 0 must be true.  No neighbors!
	}


	/** INTERFACE METHOD.  removeObjectFromArray removes a particular reference to an object into an array.  This is useful when the index is not known.
	 * @param o  is the Object to be removed
	 * @param array is an array Objects
	 * @return true if removed correctly, or false if not removed.
	 */
	protected boolean removeObjectFromArray(Object o,Object[] array) {
		int n = 0;
		while (n < array.length) {
			if (array[n] == o) {
				array[n] = null;
				return true;
			}
			n++;
		}
		return false;
	}

	/**
	 * copyArray returns a copy of the original array (static type GameBoardCell[])
	 * 
	 * @param array is the array to be copied
	 * @return a new array that is a copy of the original
	 */

	private GameBoardCell[] copyArray (GameBoardCell[] array) {
		GameBoardCell[] newArray = new GameBoardCell[array.length];
		for (int i = 0; i < newArray.length; i++ ) {
			newArray[i] = array[i];
		}
		return newArray;
	}

	/** insertedObjectIntoArray inserts an object into an array.
	 * 
	 * @param o  is the Object to be inserted
	 * @param array is an array Objects
	 * @return true if inserted correctly, or false if not inserted.
	 */
	protected boolean insertObjectIntoArray(Object o,Object[] array) {
		int n = 0;
		while (n < array.length) {
			if (array[n] == null) {
				array[n] = o;
				return true;
			}
			n++;
		}
		return false;
	}

	/**
	 * arrayIsEqual is a testing method which checks to see if the contents of two GameBoardCell arrays are equal.
	 * @param myArray is an array you want to compare with
	 * @param returnedArray is the array you want to compare myArray against
	 * @return true if the arrays are equal, false if even ONE element of the two arrays are different.
	 */
	protected boolean arrayIsEqual(GameBoardCell[] myArray, GameBoardCell[] returnedArray) {
		for (int i = 0; (i < myArray.length && i < returnedArray.length); i++) {
			if (myArray[i] != returnedArray[i]) {
				return false;
			}
		}
		return true;
	}

	/** arrayIsEqual is a testing method which checks to see if the contents of two int arrays are equal.
	 * @param myArray is an array you want to compare with
	 * @param returnedArray is the array you want to compare myArray against
	 * @return if the arrays are equal, false if even ONE element of the two arrays are different.
	 */
	protected boolean arrayIsEqual(int[] myArray, int[] returnedArray) {
		for (int i = 0; (i < myArray.length && i < returnedArray.length); i++) {
			if (myArray[i] != returnedArray[i]) {
				return false;
			}
		}
		return true;
	}

	/** Returns a string out a board representing our gameBoard. **/
	public String toString() {
		String boardString = "  | 0  1  2  3  4  5  6  7";
		boardString = boardString + "\n----------------------------";
		for (int y = 0; y < HEIGHT; y++) {
			boardString = boardString + "\n" + y + " | ";
			for (int x = 0; x < WIDTH; x++) {
				switch (board[x][y].color) {
				case BLACK: boardString = boardString + "B  "; break;
				case WHITE: boardString = boardString + "W  "; break;
				case EMPTY: boardString = boardString + ".  "; break;
				case CORNER: boardString = boardString + "X  "; break;
				}
			}
			boardString = boardString + "\n";
		}
		return boardString;
	}


	/** Returns the opposite player. Given WHITE will return BLACK (0). and vice-versa
	 * 
	 * @param player is the color of the player we will return teh opposite color of
	 * @return the color of the opposite player.
	 */
	private int otherPlayer (int player) {
		assert player == BLACK || player == WHITE: "player trying to be put into otherPlayer() is not white nor black";
		if (player == BLACK) {
			return WHITE;
		}
		return BLACK;

	}


	/** TESTING PURPOSES ONLY.  Methods below this line do not affect the functions of our GameBoard.  **/
	/** Simply used to test our GameBoard code in the main method below
	 * 
	 * @param invariant  what we are testing for (something that should evaluate to true)
	 * @param message (what we print to the console when something doesn't pass)
	 */
	public static void verify (boolean invariant, String message) {
		if (!invariant) {
			System.out.println("*** ERROR: " + message);
			Thread.dumpStack();
		}
		else {
			System.out.println("Test passed.");
		}
	}
	
	public static void main (String[] args) {
		GameBoard gb = new GameBoard(BLACK);

		System.out.println("\n== Testing otherPlayer() ==\t Total Tests: 5");
		boolean t = (gb.otherPlayer(WHITE) == BLACK);   // should return true;
		verify(t, "gb.otherPlayer(WHITE) == BLACK was not true.");

		t = (gb.otherPlayer(BLACK) == WHITE);
		verify(t, "gb.otherPlayer(BLACK) == WHITE was not true.");

		t = !(gb.otherPlayer(WHITE) == WHITE);  // should return false;
		verify(t, "gb.otherPlayer(WHITE) == WHITE was not false.");

		t = !(gb.otherPlayer(BLACK) == BLACK);
		verify(t, "gb.otherPlayer(BLACK) == BLACK was not false.");

		try {
			t = (gb.otherPlayer(CORNER) == WHITE);  // Should throw an AssertionError if run in java -ea mode.
			verify(t, "gb.otherPlayer(CORNER) == WHITE failed to throw an AssertionError if java -ea was used.");
		} catch (AssertionError a) {
			System.out.println("Test passed.  (AssertionError thrown as expected.)");
		}

		System.out.println("\n== Testing getChipColor() ==\t Total Tests: 18");

		GameBoard gb2 = new GameBoard(BLACK);
		gb2.board[2][0].color = BLACK;  // Set up a test board.
		gb2.board[6][1].color = BLACK;
		gb2.board[0][2].color = WHITE;
		gb2.board[2][2].color = WHITE;
		gb2.board[4][3].color = BLACK;
		gb2.board[3][4].color = WHITE;
		gb2.board[7][6].color = WHITE;
		gb2.board[5][7].color = BLACK;

		t = (gb2.getChipColor(2, 0) == BLACK);
		verify(t, "gb2.getChipColor(2, 0) == BLACK was not true.");

		t = (gb2.getChipColor(6, 1) == BLACK);
		verify(t, "gb2.getChipColor(6, 1) == BLACK was not true.");

		t = (gb2.getChipColor(0, 2) == WHITE);
		verify(t, "gb2.getChipColor(0, 2) == WHITE was not true.");

		t = (gb2.getChipColor(2, 2) == WHITE);
		verify(t, "gb2.getChipColor(2, 2) == WHITE was not true.");

		t = (gb2.getChipColor(4, 3) == BLACK);
		verify(t, "gb2.getChipColor(4, 3) == BLACK was not true.");

		t = (gb2.getChipColor(3, 4) == WHITE);
		verify(t, "gb2.getChipColor(3, 4) == WHITE was not true.");

		t = (gb2.getChipColor(7, 6) == WHITE);
		verify(t, "gb2.getChipColor(7, 6) == WHITE was not true.");

		t = (gb2.getChipColor(5, 7) == BLACK);
		verify(t, "gb2.getChipColor(5, 7) == BLACK was not true.");

		t = (gb2.getChipColor(5, 2) == EMPTY);
		verify(t, "gb2.getChipColor(5, 2) == EMPTY was not true.");

		t = (gb2.getChipColor(1, 5) == EMPTY);
		verify(t, "gb2.getChipColor(1, 5) == EMPTY was not true.");

		t = (gb2.getChipColor(1, 0) == EMPTY);
		verify(t, "gb2.getChipColor(1, 0) == EMPTY was not true.");

		t = (gb2.getChipColor(7, 1) == EMPTY);
		verify(t, "gb2.getChipColor(7, 1) == EMPTY was not true.");

		t = (gb2.getChipColor(6, 7) == EMPTY);
		verify(t, "gb2.getChipColor(6, 7) == EMPTY was not true.");

		t = (gb2.getChipColor(0, 1) == EMPTY);
		verify(t, "gb2.getChipColor(0, 1) == EMPTY was not true.");

		t = (gb2.getChipColor(0, 0) == CORNER);
		verify(t, "gb2.getChipColor(0, 0) == CORNER was not true.");

		t = (gb2.getChipColor(7, 0) == CORNER);
		verify(t, "gb2.getChipColor(7, 0) == CORNER was not true.");

		t = (gb2.getChipColor(7, 7) == CORNER);
		verify(t, "gb2.getChipColor(7, 7) == CORNER was not true.");

		t = (gb2.getChipColor(0, 7) == CORNER);
		verify(t, "gb2.getChipColor(0, 7) == CORNER was not true.");

		System.out.println("\n== Testing willBeCluster() ==\t Total Tests: 30");
		GameBoard gb3 = new GameBoard(BLACK);
		gb3.board[3][0].color = BLACK;  // This is the example given on page 3 of Shewchuk's documentation.
		gb3.board[2][1].color = BLACK;
		gb3.board[5][2].color = BLACK;
		gb3.board[5][3].color = BLACK;
		gb3.board[2][4].color = BLACK;
		gb3.board[6][5].color = BLACK;
		gb3.board[1][6].color = BLACK;
		gb3.board[4][7].color = BLACK;

		t = (gb3.willBeCluster(1, 0, BLACK));
		verify(t, "gb3.willBeCluster(1, 0, BLACK) was not true.");

		t = (gb3.willBeCluster(2, 0, BLACK));   
		verify(t, "gb3.willBeCluster(2, 0, BLACK) was not true.");

		t = (gb3.willBeCluster(4, 0, BLACK));   
		verify(t, "gb3.willBeCluster(4, 0, BLACK) was not true.");

		t = (gb3.willBeCluster(1, 1, BLACK));   
		verify(t, "gb3.willBeCluster(1, 1, BLACK) was not true.");

		t = (gb3.willBeCluster(3, 1, BLACK));   
		verify(t, "gb3.willBeCluster(3, 1, BLACK) was not true.");

		t = (gb3.willBeCluster(4, 1, BLACK));
		verify(t, "gb3.willBeCluster(4, 1, BLACK) was not true.");

		t = (gb3.willBeCluster(5, 1, BLACK));   
		verify(t, "gb3.willBeCluster(5, 1, BLACK) was not true.");

		t = (gb3.willBeCluster(6, 1, BLACK));   
		verify(t, "gb3.willBeCluster(6, 1, BLACK) was not true.");

		t = (gb3.willBeCluster(1, 2, BLACK));   
		verify(t, "gb3.willBeCluster(1, 2, BLACK) was not true.");

		t = (gb3.willBeCluster(2, 2, BLACK));   
		verify(t, "gb3.willBeCluster(2, 2, BLACK) was not true.");

		t = (gb3.willBeCluster(3, 2, BLACK));   
		verify(t, "gb3.willBeCluster(3, 2, BLACK) was not true.");

		t = (gb3.willBeCluster(4, 2, BLACK));   
		verify(t, "gb3.willBeCluster(4, 2, BLACK) was not true.");

		t = (gb3.willBeCluster(6, 2, BLACK));   
		verify(t, "gb3.willBeCluster(6, 2, BLACK) was not true.");

		t = (gb3.willBeCluster(4, 3, BLACK));   
		verify(t, "gb3.willBeCluster(4, 3, BLACK) was not true.");

		t = (gb3.willBeCluster(6, 3, BLACK));   
		verify(t, "gb3.willBeCluster(6, 3, BLACK) was not true.");

		t = (gb3.willBeCluster(4, 4, BLACK));   
		verify(t, "gb3.willBeCluster(4, 4, BLACK) was not true.");

		t = (gb3.willBeCluster(5, 4, BLACK));   
		verify(t, "gb3.willBeCluster(5, 4, BLACK) was not true.");

		t = (gb3.willBeCluster(6, 4, BLACK));   
		verify(t, "gb3.willBeCluster(6, 4, BLACK) was not true.");

		t = (gb3.willBeCluster(1, 5, BLACK));   
		verify(t, "gb3.willBeCluster(1, 5, BLACK) was not true.");

		t = (gb3.willBeCluster(2, 5, BLACK));   
		verify(t, "gb3.willBeCluster(2, 5, BLACK) was not true.");

		t = (gb3.willBeCluster(5, 6, BLACK));   
		verify(t, "gb3.willBeCluster(5, 6, BLACK) was not true.");

		t = !(gb3.willBeCluster(6, 0, BLACK));  
		verify(t, "gb3.willBeCluster(6, 0, BLACK) was not false.");

		t = !(gb3.willBeCluster(2, 6, BLACK));  
		verify(t, "gb3.willBeCluster(2, 6, BLACK) was not false.");

		t = !(gb3.willBeCluster(0, 4, BLACK));  
		verify(t, "gb3.willBeCluster(0, 4, BLACK) was not false.");

		t = !(gb3.willBeCluster(1, 0, WHITE));          
		verify(t, "gb3.willBeCluster(1, 0, WHITE) was not false.");

		t = !(gb3.willBeCluster(2, 0, WHITE));          
		verify(t, "gb3.willBeCluster(2, 0, WHITE) was not false.");

		t = !(gb3.willBeCluster(4, 1, WHITE));          
		verify(t, "gb3.willBeCluster(4, 1, WHITE) was not false.");

		t = !(gb3.willBeCluster(6, 1, WHITE));          
		verify(t, "gb3.willBeCluster(6, 1, WHITE) was not false.");

		t = !(gb3.willBeCluster(1, 5, WHITE));          
		verify(t, "gb3.willBeCluster(1, 5, WHITE) was not false.");

		t = !(gb3.willBeCluster(2, 5, WHITE));          
		verify(t, "gb3.willBeCluster(2, 5, WHITE) was not false.");

		System.out.println("\n== Testing objectsInArray() ==\t Total Tests: 8");
		GameBoard gb4 = new GameBoard(BLACK);
		gb4.allChips[BLACK][0] = new GameBoardCell(2, 2);
		gb4.allChips[BLACK][0].color = BLACK;
		gb4.allChips[BLACK][1] = new GameBoardCell(4, 5);
		gb4.allChips[BLACK][1].color = BLACK;
		gb4.allChips[WHITE][0] = new GameBoardCell(3, 3);
		gb4.allChips[WHITE][0].color = WHITE;
		gb4.allChips[WHITE][1] = new GameBoardCell(5, 5);
		gb4.allChips[WHITE][1].color = WHITE;
		gb4.allChips[WHITE][2] = new GameBoardCell(5, 3);
		gb4.allChips[WHITE][2].color = WHITE;

		t = (gb4.objectsInArray(gb4.allChips[BLACK]) == 2);
		verify(t, "allChips[BLACK] did not return 2.");

		t = (gb4.objectsInArray(gb4.allChips[WHITE]) == 3);
		verify(t, "allChips[WHITE] did not return 3.");

		gb4.allChips[BLACK][0] = null;  // Moving reference from 0th index to 2nd index.
		gb4.allChips[BLACK][2] = new GameBoardCell(2, 2);
		gb4.allChips[BLACK][2].color = BLACK;
		gb4.allChips[WHITE][1] = null;  // Moving reference from 1st index to 4th index.  Gaps in between!
		gb4.allChips[WHITE][4] = new GameBoardCell(5, 5);
		gb4.allChips[WHITE][4].color = WHITE;

		t = (gb4.objectsInArray(gb4.allChips[BLACK]) == 2);
		verify(t, "objectsInArray(allChips[BLACK]) did not return 2.");

		t = (gb4.objectsInArray(gb4.allChips[WHITE]) == 3);
		verify(t, "objectsInArray(allChips[WHITE]) did not return 3.");

		GameBoard gb5 = new GameBoard(BLACK);
		gb5.allChips[BLACK][7] = new GameBoardCell(2, 3);
		gb5.allChips[BLACK][7].color = BLACK;

		t = (gb5.objectsInArray(gb5.allChips[BLACK]) == 1);
		verify(t, "objectsInArray(allChips[BLACK]) did not return 1.");

		t = (gb5.objectsInArray(gb5.allChips[WHITE]) == 0);
		verify(t, "objectsInArray(allChips[WHITE]) did not return 0.");

		for (int i = 0; i < gb5.allChips[BLACK].length; i++) {  // Fill entire allChips array completely.
			gb5.allChips[BLACK][i] = new GameBoardCell(1, i);
			gb5.allChips[BLACK][i].color = BLACK;
			gb5.allChips[WHITE][i] = new GameBoardCell(2, i);
			gb5.allChips[WHITE][i].color = WHITE;
		}

		t = (gb5.objectsInArray(gb5.allChips[BLACK]) == 10);
		verify(t, "objectsInArray(allChips[BLACK]) did not return 10.");

		t = (gb5.objectsInArray(gb5.allChips[WHITE]) == 10);
		verify(t, "objectsInArray(allChips[WHITE]) did not return 10.");

		System.out.println("\n== Testing wrapDirection() ==\t Total Tests: 10");
		t = (gb5.wrapDirection(N+4) == S);
		verify(t, "wrapDirection(N+4) did not return S.");

		t = (gb5.wrapDirection(NE+4) == SW);
		verify(t, "wrapDirection(NE+4) did not return SW.");

		t = (gb5.wrapDirection(E+4) == W);
		verify(t, "wrapDirection(E+4) did not return W.");

		t = (gb5.wrapDirection(SE+4) == NW);
		verify(t, "wrapDirection(SE) did not return NW.");

		t = (gb5.wrapDirection(S+4) == N);
		verify(t, "wrapDirection(S+4) did not return N.");

		t = (gb5.wrapDirection(SW+4) == NE);
		verify(t, "wrapDirection(SW+4) did not return NE.");

		t = (gb5.wrapDirection(W+4) == E);
		verify(t, "wrapDirection(W+4) did not return E.");

		t = (gb5.wrapDirection(NW+4) == SE);
		verify(t, "wrapDirection(NW+4) did not return SE.");

		t = (gb5.wrapDirection(100) == 4);
		verify(t, "wrapDirection(100) did not return 4.");

		try {
			t = (gb5.wrapDirection(-2) == 6);
			verify(t, "wrapDirection(-2) did not return 6.  (-ed mode)");
		} catch (AssertionError e) {
			System.out.println("Test passed.  (AssertionError thrown as expected.)");
		}

		System.out.println("\n== Testing validDirection() ==\t Total Tests: 9");    
		t = gb5.arrayIsEqual(gb5.validDirection(N), new int[] {0, 1, 1, 1, 0, 1, 1, 1});
		verify(t, "gb5.validDirection(N) failed.");

		t = gb5.arrayIsEqual(gb5.validDirection(NE), new int[] {1, 0, 1, 1, 1, 0, 1, 1});
		verify(t, "gb5.validDirection(NE) failed.");

		t = gb5.arrayIsEqual(gb5.validDirection(E), new int[] {1, 1, 0, 1, 1, 1, 0, 1});
		verify(t, "gb5.validDirection(E) failed.");

		t = gb5.arrayIsEqual(gb5.validDirection(SE), new int[] {1, 1, 1, 0, 1, 1, 1, 0});
		verify(t, "gb5.validDirection(SE) failed.");

		t = gb5.arrayIsEqual(gb5.validDirection(S), new int[] {0, 1, 1, 1, 0, 1, 1, 1});
		verify(t, "gb5.validDirection(S) failed.");

		t = gb5.arrayIsEqual(gb5.validDirection(SW), new int[] {1, 0, 1, 1, 1, 0, 1, 1});
		verify(t, "gb5.validDirection(SW) failed.");

		t = gb5.arrayIsEqual(gb5.validDirection(W), new int[] {1, 1, 0, 1, 1, 1, 0, 1});
		verify(t, "gb5.validDirection(W) failed.");

		t = gb5.arrayIsEqual(gb5.validDirection(NW), new int[] {1, 1, 1, 0, 1, 1, 1, 0});
		verify(t, "gb5.validDirection(NW) failed.");

		System.out.println("\n== Testing addChip() ==\t Total Tests: 16");
		GameBoard gb6 = new GameBoard(BLACK);
		gb6.addChip(4, 5, BLACK);       // Add a BLACK chip to (4, 5).
		t = (gb6.board[4][5].color == BLACK && gb6.board[4][5].x == 4 && gb6.board[4][5].y == 5);
		verify(t, "BLACK chip was not added to GameBoard.board[4][5].");
		t = (gb6.objectsInArray(gb6.allChips[BLACK]) == 1);
		verify(t, "Number of BLACK chips counted by objectsInArray is not equal to 1.  It is " + gb6.objectsInArray(gb6.allChips[BLACK]));
		t = (gb6.objectsInArray(gb6.allChips[WHITE]) == 0);
		verify(t, "Number of WHITE chips counted by objectsInArray is not equal to 0.  It is " + gb6.objectsInArray(gb6.allChips[WHITE]));
		t = (gb6.allChips[BLACK][gb6.objectsInArray(gb6.allChips[BLACK])-1].color == BLACK && gb6.allChips[BLACK][gb6.objectsInArray(gb6.allChips[BLACK])-1].x == 4 && gb6.allChips[BLACK][gb6.objectsInArray(gb6.allChips[BLACK])-1].y == 5);
		verify(t, "BLACK chip was not added into allChips[BLACK].");

		gb6.addChip(2, 3, WHITE);       // Add a WHITE chip to (2, 3).
		t = (gb6.board[2][3].color == WHITE && gb6.board[2][3].x == 2 && gb6.board[2][3].y == 3);
		verify(t, "WHITE chip was not added to GameBoard.board[2][3].");
		t = (gb6.objectsInArray(gb6.allChips[BLACK]) == 1);
		verify(t, "Number of BLACK chips counted by objectsInArray is not equal to 1.  It is " + gb6.objectsInArray(gb6.allChips[BLACK]));
		t = (gb6.objectsInArray(gb6.allChips[WHITE]) == 1);
		verify(t, "Number of WHITE chips counted by objectsInArray is not equal to 1.  It is " + gb6.objectsInArray(gb6.allChips[WHITE]));
		t = (gb6.allChips[WHITE][gb6.objectsInArray(gb6.allChips[WHITE])-1].color == WHITE && gb6.allChips[WHITE][gb6.objectsInArray(gb6.allChips[WHITE])-1].x == 2 && gb6.allChips[WHITE][gb6.objectsInArray(gb6.allChips[WHITE])-1].y == 3);
		verify(t, "WHITE chip was not added into allChips[WHITE].");

		gb6.addChip(5, 4, BLACK);       // Add a BLACK chip to (5, 4).
		t = (gb6.board[5][4].color == BLACK && gb6.board[5][4].x == 5 && gb6.board[5][4].y == 4);
		verify(t, "BLACK chip was not added to GameBoard.board[5][4].");
		t = (gb6.objectsInArray(gb6.allChips[BLACK]) == 2);
		verify(t, "Number of BLACK chips counted by objectsInArray is not equal to 2.  It is " + gb6.objectsInArray(gb6.allChips[BLACK]));
		t = (gb6.objectsInArray(gb6.allChips[WHITE]) == 1);
		verify(t, "Number of WHITE chips counted by objectsInArray is not equal to 1.  It is " + gb6.objectsInArray(gb6.allChips[WHITE]));
		t = (gb6.allChips[BLACK][gb6.objectsInArray(gb6.allChips[BLACK])-1].color == BLACK && gb6.allChips[BLACK][gb6.objectsInArray(gb6.allChips[BLACK])-1].x == 5 && gb6.allChips[BLACK][gb6.objectsInArray(gb6.allChips[BLACK])-1].y == 4);
		verify(t, "BLACK chip was not added into allChips[BLACK].");

		gb6.addChip(3, 2, WHITE);       // Add a WHITE chip to (3, 2).
		t = (gb6.board[3][2].color == WHITE && gb6.board[3][2].x == 3 && gb6.board[3][2].y == 2);
		verify(t, "WHITE chip was not added to GameBoard.board[3][2].");
		t = (gb6.objectsInArray(gb6.allChips[BLACK]) == 2);
		verify(t, "Number of BLACK chips counted by objectsInArray is not equal to 2.  It is " + gb6.objectsInArray(gb6.allChips[BLACK]));
		t = (gb6.objectsInArray(gb6.allChips[WHITE]) == 2);
		verify(t, "Number of WHITE chips counted by objectsInArray is not equal to 2.  It is " + gb6.objectsInArray(gb6.allChips[WHITE]));
		t = (gb6.allChips[WHITE][gb6.objectsInArray(gb6.allChips[WHITE])-1].color == WHITE && gb6.allChips[WHITE][gb6.objectsInArray(gb6.allChips[WHITE])-1].x == 3 && gb6.allChips[WHITE][gb6.objectsInArray(gb6.allChips[WHITE])-1].y == 2);
		verify(t, "WHITE chip was not added into allChips[WHITE].");

		System.out.println("\n== Testing removeChip() ==\t Total Tests: 3");
		GameBoard gb7 = new GameBoard(BLACK);
		gb7.addChip(4, 5, BLACK);
		gb7.addChip(3, 2, BLACK);
		gb7.removeChip(4, 5);
		t = (gb7.board[4][5].color == EMPTY);
		verify (t, "Chip color has not been set back to default EMPTY");
		t = (gb7.board[4][5].x == 4 && gb7.board[4][5].y == 5);
		verify(t, "Coordinates of EMPTY chip at (4, 5) have been changed.");
		t = (gb7.objectsInArray(gb7.allChips[BLACK]) == 1);
		verify (t, "Number of BLACK chips counted by objectsInArray is not equal to 1.  It is " + gb7.objectsInArray(gb7.allChips[BLACK]));

		System.out.println("\n== Testing isValidMove() ==\t Total Tests: 36");
		GameBoard gb8 = new GameBoard(WHITE);
		t = !gb8.isValidMove(new Move(0, 0), WHITE); // Testing move to corner, should be false;
		verify(t,"ADD move to corner (0, 0) was not marked as invalid.");

		t = !(gb8.isValidMove(new Move(0, 7), WHITE));
		verify(t, "ADD move to corner (0, 7) was not marked as invalid.");

		t = !(gb8.isValidMove(new Move(7, 7), WHITE));
		verify(t, "ADD move to corner (7, 7) was not marked as invalid.");

		t = !(gb8.isValidMove(new Move(7, 0), WHITE));
		verify(t, "ADD move to corner (7, 0) was not marked as invalid.");

		t = !gb8.isValidMove(new Move(0, 0), BLACK); // Testing move to corner, should be false;
		verify(t,"ADD move to corner (0, 0) was not marked as invalid.");

		t = !(gb8.isValidMove(new Move(0, 7), BLACK));
		verify(t, "ADD move to corner (0, 7) was not marked as invalid.");

		t = !(gb8.isValidMove(new Move(7, 7), BLACK));
		verify(t, "ADD move to corner (7, 7) was not marked as invalid.");

		t = !(gb8.isValidMove(new Move(7, 0), BLACK));
		verify(t, "ADD move to corner (7, 0) was not marked as invalid.");

		t = (gb8.isValidMove(new Move(0, 1), WHITE));   // Testing move to goal areas.
		verify(t, "ADD WHITE chip to WHITE goal area (0, 1) was not marked as valid.");

		t = !(gb8.isValidMove(new Move(0, 1), BLACK));
		verify(t, "ADD BLACK chip to WHITE goal area (0, 1) was not marked as invalid.");

		t = (gb8.isValidMove(new Move(7, 5), WHITE));
		verify(t, "ADD WHITE chip to WHITE goal area (7, 5) was not marked as valid.");

		t = !(gb8.isValidMove(new Move(7, 5), BLACK));
		verify(t, "ADD BLACK chip to WHITE goal area (7, 5) was not marked as invalid.");

		t = !(gb8.isValidMove(new Move(3, 0), WHITE));
		verify(t, "ADD WHITE chip to BLACK goal area (3, 0) was not marked as invalid.");

		t = (gb8.isValidMove(new Move(3, 0), BLACK));
		verify(t, "ADD BLACK chip to BLACK goal area (3, 0) was marked as invalid.");

		t = !(gb8.isValidMove(new Move(1, 7), WHITE));
		verify(t, "ADD WHITE chip to BLACK goal area (1, 7) was not marked as invalid.");

		t = (gb8.isValidMove(new Move(1, 7), BLACK));
		verify(t, "ADD BLACK chip to BLACK goal area (1, 7) was marked as invalid.");

		t = gb8.isValidMove(new Move(1,1), WHITE);      // Testing move to non-goal squares.
		verify(t,"ADD WHITE chip to (1, 1) was marked as invalid.");
		gb8.addChip(1, 1, WHITE);

		t = !(gb8.isValidMove(new Move(1, 1), BLACK));  // Try to add BLACK chip on top of WHITE chip.
		verify(t, "ADD BLACK chip on top of (1, 1)'s WHITE chip was marked as valid.");

		t = !(gb8.isValidMove(new Move(1, 1), WHITE));  // Try to add WHITE chip on top of WHITE chip.
		verify(t, "ADD WHITE chip on top of (1, 1's WHITE chip was marked as valid.");

		t = (gb8.isValidMove(new Move(2, 1), WHITE));
		verify(t, "ADD WHITE chip to (2, 1) was marked as invalid.");
		gb8.addChip(2, 1, WHITE);

		t = (gb8.isValidMove(new Move(1, 3), BLACK));
		verify(t, "ADD BLACK chip to (1, 3) was marked as invalid.");
		gb8.addChip(1, 3, BLACK);

		t = !(gb8.isValidMove(new Move(1, 2), WHITE));
		verify(t, "ADD WHITE chip to (1, 2) was marked as valid.");

		t = !(gb8.isValidMove(new Move(3, 2), WHITE));
		verify(t, "ADD WHITE chip to (3, 2) was marked as valid.");

		t = (gb8.isValidMove(new Move(1, 2), BLACK));
		verify(t, "ADD BLACK chip to (1, 2) was marked as invalid.");
		gb8.addChip(1, 2, BLACK);

		gb8.addChip(5, 4, WHITE);       // Added chips for to move around.
		gb8.addChip(5, 5, BLACK);

		gb8.addChip(1, 7, BLACK);
		gb8.addChip(2, 7, BLACK);
		gb8.addChip(4, 7, BLACK);
		gb8.addChip(5, 7, BLACK);
		gb8.addChip(6, 1, BLACK);
		gb8.addChip(6, 2, BLACK);
		gb8.addChip(6, 4, BLACK);
		gb8.addChip(7, 1, WHITE);
		gb8.addChip(7, 2, WHITE);
		gb8.addChip(7, 4, WHITE);
		gb8.addChip(7, 5, WHITE);
		gb8.addChip(5, 1, WHITE);
		gb8.addChip(0, 5, WHITE);
		gb8.addChip(0, 6, WHITE);

		t = !(gb8.isValidMove(new Move(3, 3), BLACK));
		verify(t, "ADD BLACK chip to (3, 3) was marked as valid.  (10 chips)");

		t = !(gb8.isValidMove(new Move(3, 3), WHITE));
		verify(t, "ADD WHITE chip to (3, 3) was marked as valid.  (10 chips)");

		t = (gb8.isValidMove(new Move(3, 5, 5, 5), BLACK));     // Move BLACK from (5, 5) to (5, 3).
		verify(t, "STEP BLACK chip from (5, 5) to (3, 5) was marked as invalid.");

		t = !(gb8.isValidMove(new Move(0, 5, 5, 5), BLACK));// Move BLACK from (5, 5) to WHITE goal (0, 5).
		verify(t, "STEP BLACK chip from (5, 5) to (0, 5) was marked as valid.");

		t = (gb8.isValidMove(new Move(2, 0, 5 , 5), BLACK));    // Move BLACK from (5, 5) to BLACK goal (2, 0).
		verify(t, "STEP BLACK chip from (5, 5) to (2, 0) was marked as invalid.");

		t = !(gb8.isValidMove(new Move(0, 0, 5, 5), BLACK));
		verify(t, "STEP BLACK chip from (5, 5) to corner (0, 0) was marked as valid.");

		t = !(gb8.isValidMove(new Move(1, 4, 5, 5), BLACK));
		verify(t, "STEP BLACK chip from (5, 5) to (1, 4) -- cluster -- was marked as valid.");

		t = !(gb8.isValidMove(new Move(2, 4, 5, 5), BLACK));
		verify(t, "STEP BLACK chip from (5, 5) to (2, 4) -- cluster -- was marked as valid.");

		t = (gb8.isValidMove(new Move(2, 4, 5, 4), WHITE));
		verify(t, "STEP WHITE chip from (5, 4) to (2, 4) was marked as invalid.");

		t = !(gb8.isValidMove(new Move(1, 4, 5, 4), WHITE));
		verify(t, "STEP WHITE chip from (5, 4) to (1, 4) was marked as valid.");

		t = !(gb8.isValidMove(new Move(6, 6, 5, 1), BLACK));
		verify(t, "STEP BLACK chip from (5, 1) to (6, 6) -- nonexistent chip -- was marked as valid.");

		t = !(gb8.isValidMove(new Move(5, 5, 5, 5), BLACK));
		verify(t, "STEP BLACK chip from (5, 5) to (5, 5) was marked as valid.");

		System.out.println("\n== Testing stepChip() ==\t Total Tests: 6");
		GameBoard gb9 = new GameBoard(BLACK);
		gb9.addChip(1, 2, BLACK);
		gb9.stepChip(3, 4, 1, 2);
		t = (gb9.board[1][2].color == EMPTY);
		verify(t, "(1, 2) is not EMPTY.");
		t = (gb9.board[3][4].color == BLACK);
		verify(t, "(3, 4) is NOT black.");
		t = (gb9.allChips[BLACK][0].color == BLACK);
		verify(t, "allChips[BLACK][0].color is not BLACK.");
		t = (gb9.allChips[BLACK][0].x == 3);
		verify(t, "allChips[BLACK][0].x is not 3.");
		t = (gb9.allChips[BLACK][0].y == 4);
		verify(t, "allChips[BLACK][0].y is not 4.");
		t = (gb9.objectsInArray(gb9.allChips[BLACK]) == 1);
		verify(t, "objectsInArray(allChips[BLACK] is not 1.  It is " + gb9.objectsInArray(gb9.allChips[BLACK]));

		System.out.println("\n== Testing check<Direction>() ==\t Total Tests: 16");
		GameBoard gb10 = new GameBoard(BLACK);
		gb10.addChip(3, 0, BLACK);
		gb10.addChip(1, 1, BLACK);
		gb10.addChip(1, 3, BLACK);
		gb10.addChip(3, 3, BLACK);
		gb10.addChip(4, 3, BLACK);
		gb10.addChip(1, 5, BLACK);
		gb10.addChip(6, 6, BLACK);
		gb10.addChip(3, 7, BLACK);
		gb10.addChip(6, 3, BLACK);
		gb10.addChip(2, 4, WHITE);
		gb10.addChip(5, 5, WHITE);
		gb10.addChip(3, 6, WHITE);
		gb10.addChip(0, 3, WHITE);

		GameBoardCell myChip = gb10.board[3][3];
		GameBoardCell returnedChip = gb10.checkNorth(BLACK, myChip);
		t = (returnedChip.color == BLACK && returnedChip.x == 3 && returnedChip.y == 0);
		verify(t, "checkNorth() did not return [ Position: (3, 0)    Color: 0 ]. It returned " + returnedChip);

		returnedChip = gb10.checkNortheast(BLACK, myChip);
		t = (returnedChip == null);
		verify(t, "checkNortheast() did not return null.  It returned " + returnedChip);

		returnedChip = gb10.checkEast(BLACK, myChip);
		t = (returnedChip.color == BLACK && returnedChip.x == 4 && returnedChip.y == 3);
		verify(t, "checkEast() did not return [ Position: (3, 4)    Color: 0 ].  It returned " + returnedChip);

		returnedChip = gb10.checkSoutheast(BLACK, myChip);
		t = (returnedChip == null);
		verify(t, "checkSoutheast() did not return null.  It returned " + returnedChip);

		returnedChip = gb10.checkSouth(BLACK, myChip);
		t = (returnedChip == null);
		verify(t, "checkSouth() did not return null.  It returned " + returnedChip);

		returnedChip = gb10.checkSouthwest(BLACK, myChip);
		t = (returnedChip == null);
		verify(t, "checkSouthwest() did not return null.  It returned " + returnedChip);

		returnedChip = gb10.checkWest(BLACK, myChip);
		t = (returnedChip.color == BLACK && returnedChip.x == 1 && returnedChip.y == 3);
		verify(t, "checkWest() did not return [ Position: (1, 3)    Color: 0 ].  It returned " + returnedChip);

		returnedChip = gb10.checkNorthwest(BLACK, myChip);
		t = (returnedChip.color == BLACK && returnedChip.x == 1 && returnedChip.y == 1);
		verify(t, "checkNorthwest() did not return [ Position: (1, 1)    Color: 0 ].  It returned " + returnedChip);

		gb10.removeChip(3, 3);
		gb10.addChip(3, 3, WHITE);

		returnedChip = gb10.checkNorth(WHITE, myChip);
		t = (returnedChip == null);
		verify(t, "checkNorth() did not return null. It returned " + returnedChip);

		returnedChip = gb10.checkNortheast(WHITE, myChip);
		t = (returnedChip == null);
		verify(t, "checkNortheast() did not return null.  It returned " + returnedChip);

		returnedChip = gb10.checkEast(WHITE, myChip);
		t = (returnedChip == null);
		verify(t, "checkEast() did not return null.  It returned " + returnedChip);

		returnedChip = gb10.checkSoutheast(WHITE, myChip);
		t = (returnedChip.color == WHITE && returnedChip.x == 5 && returnedChip.y == 5);
		verify(t, "checSoutheast() did not return [ Position: (5, 5)    Color: 1 ].  It returned " + returnedChip);

		returnedChip = gb10.checkSouth(WHITE, myChip);
		t = (returnedChip.color == WHITE && returnedChip.x == 3 && returnedChip.y == 6);
		verify(t, "checkSouth() did not return [ Position: (3, 6)    Color: 1 ].  It returned " + returnedChip);

		returnedChip = gb10.checkSouthwest(WHITE, myChip);
		t = (returnedChip.color == WHITE && returnedChip.x == 2 && returnedChip.y == 4);
		verify(t, "checkSouthwest() did not return [ Position: (2, 4)    Color: 1 ].  It returned " + returnedChip);

		returnedChip = gb10.checkWest(WHITE, myChip);
		t = (returnedChip == null);
		verify(t, "checkWest() did not return null.  It returned " + returnedChip);

		returnedChip = gb10.checkNorthwest(WHITE, myChip);
		t = (returnedChip == null);
		verify(t, "checkNorthwest() did not return null.  It returned " + returnedChip);

		System.out.println("\n== Testing connectChip() and updateChipConnections() ==\t Total Tests: 21");
		gb10.updateChipConnections();

		myChip = gb10.board[3][0];
		GameBoardCell[] myConn = new GameBoardCell[] {null, null, null, gb10.board[6][3], null, null, null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[1][1];
		myConn = new GameBoardCell[] {null, null, null, null, gb10.board[1][3], null, null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[0][3];
		myConn = new GameBoardCell[] {null, null, null, gb10.board[3][6], null, null, null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[1][3];
		myConn = new GameBoardCell[] {gb10.board[1][1], null, null, null, gb10.board[1][5], null, null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[3][3];
		myConn = new GameBoardCell[] {null, null, null, gb10.board[5][5], gb10.board[3][6], gb10.board[2][4], null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[4][3];
		myConn = new GameBoardCell[] {null, null, gb10.board[6][3], null, null, null, null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[6][3];
		myConn = new GameBoardCell[] {null, null, null, null, gb10.board[6][6], null, gb10.board[4][3], gb10.board[3][0]};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[2][4];
		myConn = new GameBoardCell[] {null, gb10.board[3][3], null, null, null, null, null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[1][5];
		myConn = new GameBoardCell[] {gb10.board[1][3], null, null, gb10.board[3][7], null, null, null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[5][5];
		myConn = new GameBoardCell[] {null, null, null, null, null, null, null, gb10.board[3][3]};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[3][6];
		myConn = new GameBoardCell[] {gb10.board[3][3], null, null, null, null, null, null, gb10.board[0][3]};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[6][6];
		myConn = new GameBoardCell[] {gb10.board[6][3], null, null, null, null, null, null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[3][7];
		myConn = new GameBoardCell[] {null, null, null, null, null, null, null, gb10.board[1][5]};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		gb10.stepChip(4, 1, 3, 0);      // Move!  Connections should have been updated.
		gb10.updateChipConnections();

		myChip = gb10.board[4][1];
		myConn = new GameBoardCell[] {null, null, null, gb10.board[6][3], gb10.board[4][3], null, gb10.board[1][1], null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[1][1];
		myConn = new GameBoardCell[] {null, null, gb10.board[4][1], null, gb10.board[1][3], null, null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[4][3];
		myConn = new GameBoardCell[] {gb10.board[4][1], null, gb10.board[6][3], null, null, null, null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[6][3];
		myConn = new GameBoardCell[] {null, null, null, null, gb10.board[6][6], null, gb10.board[4][3], gb10.board[4][1]};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		gb10.addChip(3, 1, WHITE);
		gb10.updateChipConnections();

		myChip = gb10.board[1][1];
		myConn = new GameBoardCell[] {null, null, null, null, gb10.board[1][3], null, null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[3][1];
		myConn = new GameBoardCell[] {null, null, null, null, gb10.board[3][3], null, null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[4][1];
		myConn = new GameBoardCell[] {null, null, null, gb10.board[6][3], gb10.board[4][3], null, null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		myChip = gb10.board[3][3];
		myConn = new GameBoardCell[] {gb10.board[3][1], null, null, gb10.board[5][5], gb10.board[3][6], gb10.board[2][4], null, null};
		t = gb10.arrayIsEqual(myConn, myChip.connections);
		verify(t, "myConn does not equal myChip's connections.");

		System.out.println("\n== Testing doMove() ==\t Total Tests: 4");
		GameBoard gb11 = new GameBoard(BLACK);

		gb11.addChip(4, 0, BLACK);
		gb11.addChip(2, 1, BLACK);
		gb11.addChip(3, 2, BLACK);
		gb11.addChip(5, 2, BLACK);
		gb11.addChip(6, 3, BLACK);
		gb11.addChip(2, 4, BLACK);
		gb11.addChip(3, 5, BLACK);
		gb11.addChip(5, 5, BLACK);
		gb11.addChip(6, 6, BLACK);
		gb11.addChip(1, 7, BLACK);
		gb11.addChip(5, 6, WHITE);
		gb11.addChip(0, 6, WHITE);
		gb11.addChip(4, 5, WHITE);
		gb11.addChip(2, 5, WHITE);
		gb11.addChip(1, 4, WHITE);
		gb11.addChip(1, 1, WHITE);
		gb11.addChip(6, 1 , WHITE);
		gb11.addChip(2, 2, WHITE);
		gb11.addChip(5, 3, WHITE);
		gb11.addChip(4, 2, WHITE);

		Move move2 = new Move (1, 1);
		gb11.doMove(move2, BLACK);
		t = (gb11.board[1][1].color == WHITE);
		verify (t, "isValidMove is not working in doMove and is still adding a chip(overriding existing chip).");

		move2 = new Move (4, 3);
		gb11.doMove(move2, BLACK);
		t = (gb11.board[4][3].color == EMPTY);
		verify (t, "isValidMove is not working in doMove and is still adding a chip, even when there is clustering.");

		move2 = new Move (7, 4, 1, 4);
		gb11.doMove(move2, WHITE);
		t = (gb11.board[1][4].color == EMPTY); //Showing up as 1 (WHITE) should be EMPTY;
		verify(t, "Old chip from using StepChip has not been removed and still exists.");

		move2 = new Move (3, 7);
		gb11.doMove(move2, BLACK);
		t = gb11.board[3][7].color == EMPTY;
		verify(t, "Chip was added even when only STEPs are allowed.");


		System.out.println("\n== Testing undoMove() ==\t Total Tests: 1");

		GameBoard gb12 = new GameBoard(BLACK);

		gb12.addChip(4, 0, BLACK);
		gb12.addChip(2, 1, BLACK);
		gb12.addChip(3, 2, BLACK);
		gb12.addChip(5, 2, BLACK);
		gb12.addChip(6, 3, BLACK);
		gb12.addChip(2, 4, BLACK);
		gb12.addChip(3, 5, BLACK);
		gb12.addChip(5, 5, BLACK);
		gb12.addChip(6, 6, BLACK);
		gb12.addChip(1, 7, BLACK);
		gb12.addChip(5, 6, WHITE);
		gb12.addChip(0, 6, WHITE);
		gb12.addChip(4, 5, WHITE);
		gb12.addChip(2, 5, WHITE);
		gb12.addChip(1, 4, WHITE);
		gb12.addChip(1, 1, WHITE);
		gb12.addChip(6, 1, WHITE);
		gb12.addChip(2, 2, WHITE);
		gb12.addChip(5, 3, WHITE);
		gb12.addChip(4, 2, WHITE);

		move2 = new Move (7, 4, 1, 4);
		gb12.doMove(move2, WHITE);
		gb12.undoMove(move2);
		t = gb12.board[7][4].color == EMPTY;
		verify (t, "Undo move is not working");

		System.out.println("\n== Testing hasNetwork() ==\t Total Tests: 18");
		GameBoard gb13 = new GameBoard(BLACK);
		gb13.addChip(6, 0, BLACK);
		gb13.addChip(2, 0, BLACK);
		gb13.addChip(4, 2, BLACK);
		gb13.addChip(3, 3, BLACK);
		gb13.addChip(3, 5, BLACK);
		gb13.addChip(5, 7, BLACK);
		gb13.updateChipConnections();

		t = !gb13.hasNetwork(BLACK);
		verify(t, "BLACK should not have a network.");

		t = !gb13.hasNetwork(WHITE);
		verify(t, "WHITE should not have a network.");

		GameBoard gb14 = new GameBoard(BLACK);
		gb14.addChip(2, 0, BLACK);
		gb14.addChip(6, 0, BLACK);
		gb14.addChip(4, 2, BLACK);
		gb14.addChip(6, 5, BLACK);
		gb14.addChip(5, 5, BLACK);
		gb14.addChip(5, 7, BLACK);
		gb14.updateChipConnections();

		t = !gb14.hasNetwork(BLACK);
		verify(t, "BLACK should not have a network.");

		t = !gb14.hasNetwork(WHITE);
		verify(t, "WHITE should not have a network.");

		GameBoard gb15 = new GameBoard(BLACK);
		gb15.addChip(2, 0, BLACK);
		gb15.addChip(2, 5, BLACK);
		gb15.addChip(5, 5, BLACK);
		gb15.addChip(3, 3, BLACK);
		gb15.addChip(3, 5, BLACK);
		gb15.addChip(5, 7, BLACK);
		gb15.updateChipConnections();

		t = gb15.hasNetwork(BLACK);
		verify(t, "BLACK should have a network. This is the one my hasNetwork caught");

		t = !gb15.hasNetwork(WHITE);
		verify(t, "WHITE should not have a network.");

		GameBoard gb16 = new GameBoard(BLACK);
		gb16.addChip(6, 0, BLACK);
		gb16.addChip(4, 2, BLACK);
		gb16.addChip(2, 5, BLACK);
		gb16.addChip(3, 3, BLACK);
		gb16.addChip(3, 5, BLACK);
		gb16.addChip(2, 7, BLACK);
		gb16.updateChipConnections();

		t = !gb16.hasNetwork(BLACK);
		verify(t, "BLACK should not have a network.");

		t = !gb16.hasNetwork(WHITE);
		verify(t, "WHITE should not have a network.");

		GameBoard gb17 = new GameBoard(BLACK);
		gb17.addChip(6, 0, BLACK);
		gb17.addChip(6, 5, BLACK);
		gb17.addChip(5, 5, BLACK);
		gb17.addChip(3, 3, BLACK);
		gb17.addChip(3, 5, BLACK);
		gb17.addChip(5, 7, BLACK);
		gb17.updateChipConnections();

		t = gb17.hasNetwork(BLACK);
		verify(t, "BLACK should have a network.");
		System.out.println("passed this mileStone! :)");

		t = !gb17.hasNetwork(WHITE);
		verify(t, "WHITE should not have a network.");

		GameBoard gb18 = new GameBoard(BLACK);
		gb18.addChip(2, 0, BLACK);
		gb18.addChip(2, 5, BLACK);
		gb18.addChip(1, 3, BLACK);
		gb18.addChip(3, 3, BLACK);
		gb18.addChip(3, 5, BLACK);
		gb18.addChip(5, 5, BLACK);
		gb18.addChip(5, 7, BLACK);
		gb18.updateChipConnections();
		System.out.println(gb18);

		t = gb18.hasNetwork(BLACK);
		verify(t, "BLACK should have a network.");

		t = !gb18.hasNetwork(WHITE);
		verify(t, "WHITE should not have a network.");

		gb18.addChip(2, 3, WHITE);

		gb18.updateChipConnections();
		t = !gb18.hasNetwork(BLACK);
		verify(t, "BLACK should not have a network.");

		GameBoard gb19 = new GameBoard(BLACK);
		gb19.addChip(2, 0, BLACK);
		gb19.addChip(2, 5, BLACK);
		gb19.addChip(4, 5, BLACK);
		gb19.addChip(6, 3, BLACK);
		gb19.addChip(6, 7, BLACK);
		gb19.updateChipConnections();

		t = !gb19.hasNetwork(BLACK);
		verify(t, "BLACK should not have a network.");

		t = !gb19.hasNetwork(WHITE);
		verify(t, "WHITE should not have a network.");

		GameBoard gb20 = new GameBoard(BLACK);
		gb20.addChip(0, 1, WHITE);
		gb20.addChip(1, 0, BLACK);
		gb20.addChip(2, 1, WHITE);
		gb20.addChip(3, 0, BLACK);
		gb20.addChip(6, 1, WHITE);
		gb20.addChip(5, 0, BLACK);
		gb20.addChip(7, 1, WHITE);
		gb20.addChip(2, 3, BLACK);
		gb20.addChip(0, 3, WHITE);
		gb20.addChip(5, 3, BLACK);
		gb20.addChip(4, 3, WHITE);
		gb20.addChip(3, 5, BLACK);
		gb20.addChip(2, 4, WHITE);
		gb20.addChip(4, 5, BLACK);
		gb20.addChip(7, 4, WHITE);
		gb20.addChip(2, 7, BLACK);
		gb20.addChip(0, 6, WHITE);
		gb20.addChip(4, 7, BLACK);
		gb20.addChip(7, 6, WHITE);
		gb20.addChip(6, 7, BLACK);
		gb20.updateChipConnections();

		t = !gb20.hasNetwork(BLACK);
		verify(t, "BLACK should not have a network.");

		t = !gb20.hasNetwork(WHITE);
		verify(t, "WHITE should not have a network.");

		gb20.stepChip(6, 6, 7, 6);
		gb20.stepChip(3, 2, 2, 3);

		t = !gb20.hasNetwork(BLACK);
		verify(t, "BLACK should not have a network.");

		gb20.updateChipConnections();
		t = gb20.hasNetwork(WHITE);
		verify(t, "WHITE should have a network.");    


		System.out.println("\n== Testing allValidMoves() ==\t Total Tests: 1");

		GameBoard gb21 = new GameBoard(BLACK);
		gb21.addChip(0, 1, WHITE);
		gb21.addChip(1, 0, BLACK);
		gb21.addChip(2, 1, WHITE);
		gb21.addChip(3, 0, BLACK);
		gb21.addChip(6, 1, WHITE);
		gb21.addChip(5, 0, BLACK);
		gb21.addChip(7, 1, WHITE);
		gb21.addChip(2, 3, BLACK);
		gb21.addChip(0, 3, WHITE);
		gb21.addChip(5, 3, BLACK);
		gb21.addChip(4, 3, WHITE);
		gb21.addChip(3, 5, BLACK);
		gb21.addChip(2, 4, WHITE);
		gb21.addChip(4, 5, BLACK);
		gb21.addChip(7, 4, WHITE);
		gb21.addChip(2, 7, BLACK);
		gb21.addChip(0, 6, WHITE);
		gb21.addChip(4, 7, BLACK);
		gb21.addChip(7, 6, WHITE);
		gb21.addChip(6, 7, BLACK);
		gb21.updateChipConnections();
		System.out.println(gb21);
		gb21.allValidMoves(BLACK);


	}
}